﻿namespace Lite.Net
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using Lite.Common;

    public sealed partial class SocketManager
    {
        private readonly List<SocketMonitor> _socketMonitors = new List<SocketMonitor>();
        private readonly List<SocketListenerMonitor> _socketListeners = new List<SocketListenerMonitor>();

        private readonly object _socketsLockObject = new object();
        private readonly object _socketListenersLockObject = new object();

        private bool _isRunning;
        private bool _isPaused;

        private readonly object _stateLockObject = new object();

        public SocketManager()
        {
            Pool = new BufferPool(1024);
        }

        public void ListenOn(IPAddress address, int port)
        {
            SocketListenerMonitor newSocketListener = new SocketListenerMonitor(address, port);
            newSocketListener.SocketAccepted += new EventHandler<EventArgs<Socket>>(_socketListener_SocketAccepted);


            lock (_stateLockObject)
            {
                lock (_socketListenersLockObject)
                {
                    _socketListeners.Add(newSocketListener);
                }

                if (_isRunning)
                {
                    newSocketListener.Start();
                }
            }
        }

        private void _socketListener_SocketAccepted(object sender, EventArgs<Socket> e)
        {
            ISession newSession = AddSocket(e.Data);

            OnSessionStarted(newSession);
        }

        private ISession AddSocket(Socket newSocket)
        {
            SocketMonitor newMonitor = new SocketMonitor(this, newSocket);

            lock (_socketsLockObject)
            {
                _socketMonitors.Add(newMonitor);
            }

            return newMonitor;
        }

        private void OnSessionStarted(ISession session)
        {
            EventHandler<SessionEventArgs> sessionStarted = SessionStarted;

            if (sessionStarted != null)
            {
                sessionStarted(this, new SessionEventArgs(session));
            }
        }

        public event EventHandler<SessionEventArgs> SessionStarted;

        internal BufferPool Pool
        {
            get;
            private set;
        }
    }

    public sealed partial class SocketManager
        : IStartHaltPauseRestart
    {

        public void Pause()
        {
            lock (_stateLockObject)
            {
                if (!_isRunning)
                {
                    throw new InvalidOperationException("Cannot pause an instance that is not started.");
                }
                else
                {
                    _isPaused = true;
                }

                lock (_socketListenersLockObject)
                {
                    _socketListeners.ForEach(socketListener => socketListener.Pause());
                }
            }
        }

        public void Restart()
        {
            lock (_stateLockObject)
            {
                if (!_isRunning)
                {
                    throw new InvalidOperationException("Cannot resume an instance that is not started.");
                }
                else
                {
                    _isPaused = false;
                }

                lock (_socketListenersLockObject)
                {
                    _socketListeners.ForEach(socketListener => socketListener.Restart());
                }
            }
        }
    }

    public sealed partial class SocketManager
        : IStartHalt
    {
        public void Start()
        {
            lock (_stateLockObject)
            {
                _isRunning = true;

                lock (_socketListenersLockObject)
                {
                    _socketListeners.ForEach(socketListener => socketListener.Start());
                }
            }
        }

        public void Halt()
        {
            lock (_stateLockObject)
            {
                _isRunning = false;
                _isPaused = false;

                lock (_socketListenersLockObject)
                {
                    _socketListeners.ForEach(socketListener => socketListener.Halt());
                }
            }
        }

        public StartHaltState State
        {
            get
            {
                lock (_stateLockObject)
                {
                    if (_isRunning)
                    {
                        if (_isPaused)
                        {
                            return StartHaltState.Paused;
                        }
                        else
                        {
                            return StartHaltState.Started;
                        }
                    }
                    else
                    {
                        bool isListening = false;

                        isListening = _socketListeners.Any(socketListener => socketListener.State == StartHaltState.Halting);

                        if (isListening)
                        {
                            return StartHaltState.Halting;
                        }
                        else
                        {
                            return StartHaltState.Halted;
                        }
                    }
                }
            }
        }
    }
}
